package com.swak.im.worker;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import com.swak.Constants;
import com.swak.OS;
import com.swak.im.Im;
import com.swak.im.ImConfig;
import com.swak.im.ImConstants;
import com.swak.im.ImWorker;
import com.swak.im.enums.Role;
import com.swak.im.mapping.MappingService;
import com.swak.im.queue.QueueService;
import com.swak.im.storage.StorageService;
import com.swak.metrics.MetricsFactory;
import com.swak.metrics.NamedMetrics;
import com.swak.registry.RegistryService;
import com.swak.registry.URL;
import com.swak.utils.Maps;
import com.swak.vertx.invoker.GenericFluxInvoker;

/**
 * Im 服务器: im - module - cs - worker
 * 
 * @author 618lf
 */
@SuppressWarnings("unchecked")
public abstract class AbstractImWorker implements ImConstants, ImWorker {

	protected final Im im;
	protected final StorageService storageService;
	protected final RegistryService registryService;
	protected final MappingService mappingService;
	protected final QueueService queueService;
	protected final MetricsFactory metricsFactory;
	protected final Object service;
	protected final Method method;
	protected final GenericFluxInvoker invoker;
	protected final AtomicBoolean state = new AtomicBoolean(false);
	protected final NamedMetrics<Object> namedMetrics;

	protected URL oldUrl;
	protected URL url;
	protected ImConfig imConfig;
	protected ImConfig.Config config;
	protected volatile boolean opened;

	// 数据队列
	protected String queue;

	public AbstractImWorker(ImConfig config, Im im, StorageService storageService, RegistryService registryService,
			MappingService mappingService, QueueService queueService, MetricsFactory metricsFactory) {
		this.im = im;
		this.imConfig = config;
		this.storageService = storageService;
		this.registryService = registryService;
		this.mappingService = mappingService;
		this.queueService = queueService;
		this.metricsFactory = metricsFactory;
		this.service = this.im.getService();
		this.method = im.getExecuteMethod();
		this.invoker = new GenericFluxInvoker(this.service);

		// 刷新参数
		this.refresh(config);

		// Job.Module.WechatJob.Dispatcher
		this.namedMetrics = this.metricsFactory != null
				? (NamedMetrics<Object>) this.metricsFactory
						.createNamedMetrics("Im" + Constants.MODULE_SEPARATE + this.imConfig.getModule()
								+ Constants.MODULE_SEPARATE + this.im.getId() + Constants.MODULE_SEPARATE + "worker")
				: null;
	}

	/**
	 * 配置刷新
	 */
	@Override
	public void refresh(ImConfig config) {
		this.imConfig = config;
		this.config = this.imConfig.getConfigs().get(im.getId());
		this.opened = this.getRefreshAbleOpened();
		this.oldUrl = this.url;
		this.url = this.initUrl(null);
		this.queue = this.queueService.parseQueue(this.url);
		this.doRefresh();
		this.reStart();
	}

	/**
	 * 执行刷新操作
	 */
	protected void doRefresh() {
	}

	/**
	 * 重新启动： 已启动的才需要重新启动
	 */
	protected void reStart() {
		if (this.state.get()) {
			this.stop();
			this.start();
		}
	}

	/**
	 * 初始化 URL 信息
	 * 
	 * @return
	 */
	protected URL initUrl(Map<String, String> specificParameters) {
		Map<String, String> parameters = Maps.newHashMap();
		parameters.put(Constants.Application_KEY, this.imConfig.getApplicationName());
		parameters.put(Constants.Module_Key, this.imConfig.getModule());
		parameters.put(Constants.Service_Key, this.im.getId());
		parameters.put(Constants.Category_Key, Role.Worker.name());
		parameters.put(Worker_key, this.im.getService().getClass().getName());
		parameters.put(Weight_key, String.valueOf(this.getRefreshAbleWeight()));
		parameters.put(Open_key, String.valueOf(this.getRefreshAbleOpened()));
		if (specificParameters != null && !specificParameters.isEmpty()) {
			parameters.putAll(specificParameters);
		}
		return new URL(Constants.Registry_Im_Key, OS.ip(), 0, Constants.Registry_Im_Worker_Key, parameters);
	}

	/**** 可刷新的参数 ****/
	protected Integer getRefreshAbleWeight() {
		if (this.config != null && this.config.getWeight() != null) {
			return this.config.getWeight();
		}
		return this.im.getWeight();
	}

	protected Boolean getRefreshAbleOpened() {
		if (this.config != null && this.config.getOpened() != null) {
			return this.config.getOpened();
		}
		return this.im.isOpened();
	}

	@Override
	public URL getURL() {
		return url;
	}

	@Override
	public void start() {
		if (state.get()) {
			return;
		}
		this.registryService.register(this.url);
		this.state.set(true);
	}

	@Override
	public void stop() {
		if (!state.get()) {
			return;
		}
		URL unregisterURL = this.oldUrl != null ? this.oldUrl : this.url;
		this.registryService.unregister(unregisterURL);
		this.state.set(false);
	}
}